package main

import (
	"fmt"
	"reflect"
)

// 使用反射来遍历结构的字段，
//调用结构体的方法，
//并获取结构体标签的值

type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Score float32
	Sex   string
}

func (m Monster) Print() {
	fmt.Println("------start------")
	fmt.Println(m)
	fmt.Println("-------end-------")
}

func (m Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}

func (m Monster) Set(name string, age int, score float32, sex string) {
	m.Name = name
	m.Age = age
	m.Score = score
	m.Sex = sex
}

func main() {
	var m = Monster{
		Name:  "牛魔王",
		Age:   500,
		Score: 30.6,
	}
	TestStruct(m)
}

func TestStruct(s interface{}) {
	rType := reflect.TypeOf(s)
	rVal := reflect.ValueOf(s)
	kd := rVal.Kind()
	if kd != reflect.Struct {
		fmt.Println("need struct")
		return
	}
	// 获取到该结构体有几个字段
	num := rVal.NumField()
	fmt.Printf("struct has %d fields\n", num)

	for i := 0; i < num; i++ {
		val := rVal.Field(i)
		tagVal := rType.Field(i).Tag.Get("json")
		// 如果有标签，就显示，否则就显示名字
		if tagVal != "" {
			fmt.Printf("Field %d: %v = %v\n", i, tagVal, val)
		}
	}

	// 获取到该结构体有几个方法
	methodNum := rVal.NumMethod()
	fmt.Printf("struct has %d method\n", methodNum)

	for i := 0; i < methodNum; i++ {
		fmt.Printf("Method %v:\n", rType.Method(i).Name)
	}

	// 使用反射调用第2个方法，即Print
	rVal.Method(1).Call(nil)

	// 调用结构体中的第二个方法Get(n1, n2)
	var params []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(30))
	res := rVal.Method(0).Call(params)
	fmt.Println("res =", res[0].Interface().(int))
}
